package com.hyou.starter.autoconfigure.transaction;

import java.util.List;
import java.util.Properties;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.interceptor.TransactionInterceptor;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import com.hyou.starter.autoconfigure.datasource.MyDataSource;

@Configuration
public class TransactionBaseConfiguration {
    
    private static final Logger log = LoggerFactory.getLogger(TransactionBaseConfiguration.class);
    
    private static final String TRANS_ATTR_FOR_BASE_QUERY = "PROPAGATION_SUPPORTS,readOnly";
    
    @Bean
    @ConditionalOnMissingBean(name = "txManager")
    public PlatformTransactionManager txManager(MyDataSource configDataSource) {
        DataSource dataSource = configDataSource.getDataSourceMap().get(configDataSource.getDefaultDataSource());
        return new DataSourceTransactionManager(dataSource);
    }
    
    /**
     * @return 事务传播规则配置
     */
    @Bean
    @ConditionalOnMissingBean(name = "transactionAttributesProp")
    @ConfigurationProperties(prefix = "hyou.transaction")
    public TransactionAttributesProp transactionAttributesProp() {
        return new TransactionAttributesProp();
    }
    
    /**
     * @return 对需要进行事务管理的Service Bean添加事务管理AOP
     */
    @Bean
    @ConditionalOnMissingBean(name = "beanNameAutoProxyProp")
    @ConfigurationProperties(prefix = "hyou.transaction.bean-name-auto-proxy-creator")
    public BeanNameAutoProxyProp beanNameAutoProxyProp() {
        return new BeanNameAutoProxyProp();
    }
    
    @Bean
    @ConditionalOnMissingBean(name = "txInterceptor")
    public TransactionInterceptor txInterceptor(
            @Qualifier("txManager") PlatformTransactionManager txManager,
            @Qualifier("transactionAttributesProp") TransactionAttributesProp transactionAttributesProp) {
        
        log.info("transactionAttributesProp : {}", transactionAttributesProp);
        
        TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
        transactionInterceptor.setTransactionManager(txManager);
        
        Properties transactionAttrProp;
        List<TransactionAttributeItemProp> attrLst = transactionAttributesProp.getAttributes();
        
        if (CollectionUtils.isEmpty(attrLst)) {
            transactionAttrProp = makeDefaultTransactionAttrProp();
        } else {
            transactionAttrProp = makeTransactionAttrProp(attrLst);
        }
        
        transactionAttrProp.forEach((key, value) -> log.info("used transaction attribute: {} : {}", key, value));
        
        transactionInterceptor.setTransactionAttributes(transactionAttrProp);
        return transactionInterceptor;
    }
    
    /**
     * 根据Bean名规则和包名规则进行Bean事务代理匹配，符合配置规则的Bean会被事务托管。
     * 
     * @return 对事务进行托管的Bean拦截代理
     */
    @Bean
    @ConditionalOnMissingBean(name = "beanNameAutoProxyCreator")
    public MyBeanNameAutoProxyCreator beanNameAutoProxyCreator(
            @Qualifier("beanNameAutoProxyProp") BeanNameAutoProxyProp beanNameAutoProxyProp) {
        log.info("beanNameAutoProxyProp : {}", beanNameAutoProxyProp);
        
        List<String> beanNames = beanNameAutoProxyProp.getBeanNames();
        Assert.notEmpty(beanNames, "hyou.transaction.bean-name-auto-proxy-creator.bean-names 不能配置为空");
        
        List<String> packageNames = beanNameAutoProxyProp.getPackageNames();
        Assert.notEmpty(packageNames, "hyou.transaction.bean-name-auto-proxy-creator.package-names 不能配置为空");
        
        String[] beanNamesArr = new String[beanNames.size()];
        String[] packageNamesArr = new String[packageNames.size()];
        
        beanNamesArr = beanNames.toArray(beanNamesArr);
        packageNamesArr = packageNames.toArray(packageNamesArr);
        
        MyBeanNameAutoProxyCreator beanNameAutoProxyCreator = new MyBeanNameAutoProxyCreator();
        beanNameAutoProxyCreator.setBeanNames(beanNamesArr);
        beanNameAutoProxyCreator.setPackageNames(packageNamesArr);
        beanNameAutoProxyCreator.setInterceptorNames("txInterceptor");
        return beanNameAutoProxyCreator;
    }
    
    /**
     * @return 默认的事务管理属性配置
     */
    private Properties makeDefaultTransactionAttrProp() {
        Properties transactionAttrProp = new Properties();
        transactionAttrProp.setProperty("get*", TRANS_ATTR_FOR_BASE_QUERY);
        transactionAttrProp.setProperty("list*", TRANS_ATTR_FOR_BASE_QUERY);
        transactionAttrProp.setProperty("count*", TRANS_ATTR_FOR_BASE_QUERY);
        transactionAttrProp.setProperty("nt*", "PROPAGATION_REQUIRES_NEW,-Exception");
        transactionAttrProp.setProperty("nv*", "PROPAGATION_NEVER,-Exception");
        transactionAttrProp.setProperty("*", "PROPAGATION_REQUIRED,-Exception");
        return transactionAttrProp;
    }
    
    /**
     * 根据配置文件中的设置项实现事务管理属性配置
     * 
     * @param attrLst 事务管理属性
     * @return 事务管理属性配置结果
     */
    private Properties makeTransactionAttrProp(List<TransactionAttributeItemProp> attrLst) {
        Properties transactionAttrProp = new Properties();
        
        String methodReg;
        String propagation;
        for (TransactionAttributeItemProp item : attrLst) {
            methodReg = item.getMethodReg();
            propagation = item.getPropagation();
            
            Assert.hasLength(methodReg, "methodReg 属性不能为空");
            Assert.hasLength(propagation, "propagation 属性不能为空");
            
            transactionAttrProp.setProperty(methodReg, propagation);
        }
        
        return transactionAttrProp;
    }
    
}
